import os
from enum import Enum
from dataclasses import dataclass
from bs4 import BeautifulSoup, Comment
from support.utils.toolkit import Log

@dataclass
class NodeData:
    tag: str
    text: str = None
    link: str = None
    children:list = None
    
    def __str__(self):
        return "<{}> | {} | ({})".format(self.tag, self.text, self.link)
    
class HtmlHandler():
    INVALID_TAG_LIST = ['style', 'script', 'head', 'meta', 'link', 'nav', 'noscript', 'footer']
    SINGLE_TEXT_NODE_LIST = ['h1','h2','h3','p','span']
    LINK_NODE_LIST = ['a']
    DEFAULT_TITLE = "BLANK TITLE"
    BLOCK_LIST = ['div']
    FENS = Enum("FENS",{
        'START':'F-S',
        'END':'F-E'
    })
    MARKDOWN_MAP = Enum("PREFIX",{
        'h1':f"# {{text}}",
        'h2':f"## {{text}}",
        'h3':f"### {{text}}",
        'li':f"+ {{text}}",
        'u':f"- {{text}}",
        'a':f"[{{text}}]({{link}})",
        'span': f"*{{text}}*"
    })

    def __init__(self):
        self.isSkipped = False
        self.datapkg = None
    
    def extract(self, html):
        self.isSkipped = False
        soup = BeautifulSoup(html, "html.parser")
        for tag in soup(HtmlHandler.INVALID_TAG_LIST):
            tag.decompose()
        self.datapkg = self.__cleanDataList__(self.__recursiveExtract__(soup))
        # self.dump(self.datapkg, filename="node")

    def clean(self):
        self.datapkg.clear()

    def __recursiveExtract__(self, parentNode):
        nodelist = []
        for node in parentNode.children:
            if(not node.name):
                continue
            data = NodeData(node.name)
            if(node.name in HtmlHandler.LINK_NODE_LIST):
                data.text = ' '.join(t.strip() for t in node.find_all(text=True, recursive=True) if  not isinstance(t, Comment) and t.strip()).strip()
                data.link = node.get('href')
            else:
                data.text = ' '.join(t.strip() for t in node.find_all(text=True, recursive=False) if  not isinstance(t, Comment) and t.strip()).strip()
            if(len(list(node.children)) and node.name not in HtmlHandler.LINK_NODE_LIST):
                data.children = self.__recursiveExtract__(node)
            nodelist.append(data)
        return nodelist

    def __cleanDataList__(self, datalist, *, needFens=False):
        cleanList = []
        for node in datalist:
            data = None
            childrenlist = None
            if(node.children):
                childrenlist = self.__cleanDataList__(node.children, needFens=len(datalist) > 1)
            if(node.text):
                data = NodeData(node.tag)
                data.text = node.text
                data.link = node.link
            elif(childrenlist and needFens):
                # need empty parent node
                data = NodeData(HtmlHandler.FENS.START.value)
            if(data):
                cleanList.append(data)
            if(childrenlist):
                cleanList.extend(childrenlist)
            if(needFens and childrenlist):
                cleanList.append(NodeData(HtmlHandler.FENS.END.value))
        return cleanList

    def paragraphing(self, fenslevel=2):
        if(fenslevel < 1):fenslevel = 2
        context = []
        curFensLevel = 0
        for node in self.datapkg:
            if(node.tag == HtmlHandler.FENS.START.value):
                curFensLevel += 1
            elif(node.tag == HtmlHandler.FENS.END.value):
                curFensLevel -= 1
            else:
                context.append(self.__markdown__(node.tag, node.text, link=self.__shortlink__(node.link)))
            if(curFensLevel == fenslevel - 1 and node.tag == HtmlHandler.FENS.END.value):
                yield context
                context.clear()
        if(context):
            yield context
    
    def __shortlink__(self, url):
        # return "https://ray0728.cn/abc" if url else None
        return url

    def __dump__(self, pkg, tab, *, filehander=None):
        for node in pkg:
            line = "{}{}".format(tab * '\t', node)
            if(filehander):
                filehander.write(line)
                filehander.write('\n')
            else:
                Log.i(line)
            if(node.children):
                tab += 1
                self.__dump__(node.children, tab, filehander=filehander)
                tab -= 1
                
    def dump(self, datalist, tab=0, *, filename=None):
        f = None
        if(filename):
            f = open(os.path.join(os.path.expanduser('~'),'{}.txt'.format(filename)), 'w', encoding="utf-8", errors="ignore")
        self.__dump__(datalist, tab, filehander=f)
        if(f):
            f.close()

    def __markdown__(self, tag, text, *, link=None):
        desc = ""
        if(tag in HtmlHandler.MARKDOWN_MAP.__members__):
            desc = HtmlHandler.MARKDOWN_MAP[tag].value.format(text=text, link=link)
        else:
            desc = text
        return desc

    def __hasChildren__(self, node):
        return any(child.name for child in node.children)
